﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Monti.Docking;
using Monti.MVVM;
using Monti.MVVM.Services;
using Newtonsoft.Json;
using Peter.Common;

namespace Peter.Logic
{
   /// <summary>
   /// The main view model for Peter.
   /// </summary>
   public class MainViewModel : ViewModelBase, IPeterHost
   {
      #region Fields

      private object m_ActiveContent;
      private string m_WindowTitle;
      private RelayCommand m_CmdLoaded;
      private RelayCommand m_CmdNew;
      private RelayCommand m_CmdOpen;
      private RelayCommand m_CmdClose;
      private RelayCommand m_CmdSave;
      private FileExplorerViewModel m_FileExplorer;
      private CustomTemplateSelector m_DockingTemplateSelector;
      private DockGroup m_LeftToolWindows;

      #endregion

      #region Setup

      /// <summary>
      /// Initializes a new view model for peter.
      /// </summary>
      public MainViewModel ()
      {
         this.WindowTitle = "Peter";
         this.PluginManager = new PluginManager ();

         if (File.Exists ("PeterData.dat"))
         {
            try
            {
               using (var reader = new StreamReader ("PeterData.dat"))
               {
                  var jReader = new JsonTextReader (reader);
                  var serializer = new JsonSerializer ();
                  Options = serializer.Deserialize <OptionsManager> (jReader);
               }
               return;
            }
            catch (Exception ex)
            {
               // TODO: Write to console...
            }
         }

         Options = new OptionsManager ();
      }

      /// <summary>
      /// Adds the known datatemplates.
      /// </summary>
      public void AddKnownDataTemplates ()
      {
         var mainWindow = Application.Current.MainWindow;
         //this.DocumentHeaderTemplateSelector.DataTemplates.Add (typeof (EditorViewModel),
         //   mainWindow.FindResource ("EditorHeaderTemplate") as DataTemplate);
         //this.DocumentHeaderTemplateSelector.DataTemplates.Add (typeof (ImageViewModel),
         //   mainWindow.FindResource ("EditorHeaderTemplate") as DataTemplate);

         this.DockingTemplateSelector.DataTemplates.Add (typeof (EditorViewModel),
            mainWindow.FindResource ("EditorTemplate") as DataTemplate);
         this.DockingTemplateSelector.DataTemplates.Add (typeof (ImageViewModel),
            mainWindow.FindResource ("ImageViewerTemplate") as DataTemplate);

         this.DockingTemplateSelector.DataTemplates.Add (typeof (FileExplorerViewModel),
            mainWindow.FindResource ("FileExplorerTemplate") as DataTemplate);
         //this.AnchorableHeaderTemplateSelector.DataTemplates.Add (typeof (FileExplorerViewModel),
         //   mainWindow.FindResource ("FileExplorerHeaderTemplate") as DataTemplate);
         //this.AnchorableTitleTemplateSelector.DataTemplates.Add (typeof (FileExplorerViewModel),
         //   mainWindow.FindResource ("FileExplorerHeaderTemplate") as DataTemplate);
      }

      /// <summary>
      /// Event that occurs when the main window is loaded.
      /// </summary>
      /// <param name="obj"></param>
      private void OnLoaded (object obj)
      {
         GuiDispatcher = Dispatcher.CurrentDispatcher;
         this.m_FileExplorer = new FileExplorerViewModel ();
         ServiceManager.RegisterSingleton<IPeterHost, MainViewModel> (this);
         ServiceManager.RegisterSingleton<IPeterOptions, OptionsManager> (Options);

         this.AddKnownDataTemplates ();
         this.PluginManager.LoadPlugins ();

         this.LeftToolWindows = new DockGroup ();
         this.LeftToolWindows.Items.Add (this.m_FileExplorer);
      }

      #endregion

      #region Properties

      #region Docking

      /// <summary>
      /// Gets or Sets the active content.
      /// </summary>
      public object ActiveContent
      {
         get { return this.m_ActiveContent; }
         set
         {
            this.m_ActiveContent = value;
            this.RaisePropertyChanged ("ActiveContent");
         }
      }

      /// <summary>
      /// Gets the list of documents.
      /// </summary>
      public DockGroup Documents { get; set; }

      /// <summary>
      /// Gets or Sets the active content.
      /// </summary>
      public DockGroup LeftToolWindows
      {
         get { return this.m_LeftToolWindows; }
         set
         {
            this.m_LeftToolWindows = value;
            this.RaisePropertyChanged ("LeftToolWindows");
         }
      }

      /// <summary>
      /// Gets the template selector.
      /// </summary>
      public CustomTemplateSelector DockingTemplateSelector
      {
         get { return this.m_DockingTemplateSelector; }
         set
         {
            this.m_DockingTemplateSelector = value;
            this.RaisePropertyChanged ("DockingTemplateSelector");
         }
      }

      #endregion

      /// <summary>
      /// Gets the plugin manager.
      /// </summary>
      public PluginManager PluginManager { get; private set; }

      /// <summary>
      /// Gets the options for peter.
      /// </summary>
      public static OptionsManager Options { get; private set; }

      /// <summary>
      /// Gets the main thread's dispatcher object.
      /// </summary>
      public static Dispatcher GuiDispatcher { get; private set; }

      /// <summary>
      /// Gets the main window title.
      /// </summary>
      public string WindowTitle
      {
         get { return this.m_WindowTitle; }
         private set
         {
            this.m_WindowTitle = value;
            this.RaisePropertyChanged ("WindowTitle");
         }
      }

      /// <summary>
      /// Gets the list of menu items.
      /// </summary>
      public List<PeterMenuItem> MenuItems
      {
         get { return this.CreateMenuItems (); }
      }

      /// <summary>
      /// Gets the list of menu items.
      /// </summary>
      public List<PeterToolbarItem> ToolbarItems
      {
         get { return this.CreateToolbarItems (); }
      }

      #endregion

      #region Commands

      /// <summary>
      /// Gets the command that is executed when the loaded event occurs.
      /// </summary>
      public ICommand LoadedCommand
      {
         get { return this.m_CmdLoaded ?? (this.m_CmdLoaded = new RelayCommand (this.OnLoaded)); }
      }

      /// <summary>
      /// Gets the new command
      /// </summary>
      public ICommand NewCommand
      {
         get { return this.m_CmdNew ?? (this.m_CmdNew = new RelayCommand (this.OnNew)); }
      }

      /// <summary>
      /// Command to create a new editor.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnNew (object obj)
      {
         this.AddDocument (new EditorViewModel ());
      }

      /// <summary>
      /// Gets the open command
      /// </summary>
      public ICommand OpenCommand
      {
         get { return this.m_CmdOpen ?? (this.m_CmdOpen = new RelayCommand (this.OnOpen)); }
      }

      /// <summary>
      /// Command to open a new document.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnOpen (object obj)
      {
         // TODO: Show initial directory.
         var service = ServiceManager.Resolve <IOpenFileService> ();
         service.AllowMultipleSelection = false;
         if (service.ShowDialog ())
         {
            var fileInfo = new FileInfo (service.FileName);
            if (Options.ExtensionOpeners.ContainsKey (fileInfo.Extension))
            {
               var pluginName = Options.ExtensionOpeners[fileInfo.Extension];
               var plugin = this.PluginManager.GetPlugin (pluginName) as IFileOperations;
               if (plugin != null)
               {
                  plugin.Open (service.FileName);
                  return;
               }
            }
            var editor = new EditorViewModel ();
            editor.Open (service.FileName);
            this.AddDocument (editor);
         }
      }

      /// <summary>
      /// Gets the closing command to execute.
      /// </summary>
      public ICommand ClosingCommand
      {
         get { return this.m_CmdClose ?? (this.m_CmdClose = new RelayCommand (this.OnClose)); }
      }

      /// <summary>
      /// Occurs when the main window is closing.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnClose (object obj)
      {
         using (var writer = new StreamWriter ("PeterData.dat"))
         {
            var jWriter = new JsonTextWriter (writer) { Formatting = Formatting.Indented, Indentation = 3 };
            var serializer = new JsonSerializer ();
            serializer.Serialize (jWriter, Options);
         }
      }

      /// <summary>
      /// Gets the Save command.
      /// </summary>
      public ICommand SaveCommand
      {
         get { return this.m_CmdSave ?? (this.m_CmdSave = new RelayCommand (this.Save, this.CanSave)); }
      }

      /// <summary>
      /// Executes the save command.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void Save (object obj)
      {
         var fileItem = this.ActiveContent as IFileOperations;
         if (fileItem != null)
            fileItem.Save ();
      }

      /// <summary>
      /// Checks to see if the save command can execute.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      /// <returns>True if command can execute, otherwise false.</returns>
      private bool CanSave (object obj)
      {
         var fileItem = this.ActiveContent as IFileOperations;
         return fileItem != null && fileItem.CanSave ();
      }

      #endregion

      #region Methods

      /// <summary>
      /// Adds a document item to peter.
      /// </summary>
      /// <param name="item">Document item to add.</param>
      public void AddDocument (object item)
      {
         if (this.ActiveContent == null)
            this.Documents.Items.Add (item);
         else
         {
            var dockItem = this.m_ActiveContent as IDockGroupAware;
            if (dockItem != null)
               dockItem.DockGroup.Items.Add (item);
            else
               this.Documents.Items.Add (item);
         }
      }

      /// <summary>
      /// Creates a list of menu items.
      /// </summary>
      /// <returns></returns>
      private List<PeterMenuItem> CreateMenuItems ()
      {
         var menuItemsList = new List<PeterMenuItem> ();
         var mainMenuItem = new PeterMenuItem ("File");
         var menuItem = new PeterMenuItem ("Exit")
                           {
                              Command = new RelayCommand (x => Application.Current.MainWindow.Close ()),
                              IconUrl = "/Peter.Common;Component/Images/Exit.png"
                           };
         mainMenuItem.Children.Add (menuItem);
         menuItemsList.Add (mainMenuItem);

         menuItemsList.Add (new PeterMenuItem ("Edit"));

         mainMenuItem = new PeterMenuItem ("View");
         menuItem = new PeterMenuItem ("File Explorer")
                       {
                          Command = new RelayCommand (this.ShowHideFileExplorer)
                          // TODO: Add Icon
                       };
         mainMenuItem.Children.Add (menuItem);
         menuItemsList.Add (mainMenuItem);

         return menuItemsList;
      }

      private void ShowHideFileExplorer (object obj)
      {
      }

      /// <summary>
      /// Adds a main menu for the given plugin.
      /// </summary>
      /// <param name="menuItem">Main menu to add.</param>
      /// <param name="pluginName">Name of plugin to add it to.</param>
      public void AddMainMenu (PeterMenuItem menuItem, string pluginName)
      {
      }

      public void AddMenuItem (PeterMenuItem menuItem, LinkedList <string> addToStructure)
      {

      }

      /// <summary>
      /// Creates a list of toolbar items.
      /// </summary>
      /// <returns></returns>
      private List<PeterToolbarItem> CreateToolbarItems ()
      {
         var toolbarItemList = new List<PeterToolbarItem> ();
         var item = new PeterToolbarItem ("New")
                       {
                          IconUrl = "/Peter.Common;Component/Images/New.png",
                          Command = this.NewCommand
                       };
         toolbarItemList.Add (item);

         item = new PeterToolbarItem ("Open")
         {
            IconUrl = "/Peter.Common;Component/Images/open.png",
            Command = this.OpenCommand
         };
         toolbarItemList.Add (item);

         item = new PeterToolbarItem ("Save")
         {
            IconUrl = "/Peter.Common;Component/Images/Save.png",
            Command = this.SaveCommand
         };
         toolbarItemList.Add (item);
         return toolbarItemList;
      }

      #endregion
   }
}
